/**
 * *********************** ******* ***********************************
 *
 *  
 * 
 *  
 * *******************************************************************
 */

package com.arges.web.common;

import java.util.Arrays;
import java.util.List;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;

import com.arges.web.domain.tableutil.JoinedTable;

/**
 * 混合查询
 *
 * 
 * @version Ver 3.0
 * @since Ver 3.0
 * @Date 2013-1-22
 *
 */
@SuppressWarnings("unchecked")
public class QueryMixture
{

	/*
	 * 查询条件, 可支持跨表查询，不过需要设置 joinedTables属性 (目前仅支持跨2张表的查询，即本表可查其关联表的关联表，而不可查关联表的关联表的关联表）
	 * 用符号"."来分割表和字段(字段和表用实体里面的属性名表示即可)。 如查询 ChannelOrgRelation中的Device中的Organization中的name, 即所属设备的组织名称， 可用
	 * Restrictions.eq("device.organization.name", "西湖区")
	 */
	private List<Criterion> conditions;

	/*
	 * 返回哪些列，不设置 joinedTables也可支持查询非本表字段，不过表与表的类型默认为 inner join， 如果设置了joinedTable则使用设置的joinedTable
	 * 用符号"."来分割表和字段(字段和表用实体里面的属性名表示即可)。 如只想要获取ChannelOrgRelation中的Device中的Organization中的name，即所属设备的组织名称， 可用new
	 * QueryColumn("device.organization.name")， 1.) 想要将结果作为对象返回，则需要设置resultClass, 同时保证QueryColumn里面的别名跟
	 * resultClass的set方法对应，查询后会调用该set方法 2.) 否则，需要设置toObj = false
	 */
	private List<QueryColumn> queryCols;

	/*
	 * 用于跨表查询的别名表，设置该属性后，可支持 conditions的跨表查询，以及支持 queryCols的非 inner join跨表返回列（目前仅支持跨2张表的查询） 用符号"."来分割表（表用实体里面的属性名表示即可）。
	 * 如查询或获取 ChannelOrgRelation中的Device中的Organization中的name，即所属设备的组织名称， 可用new JoinedTable("device.organization")
	 */
	private List<JoinedTable> joinedTables;

	// 排序
	private List<Order> orders;

	// 从第几条数据开始获取
	private Integer startIndex;

	// 一次性获取多少条件数据
	private Integer size;

	// 作为本表的实体类
	private Class pojoClass;

	// 是否将查询的结果转化为 对象，该属性只有在 queryCols有设置的 情况下才有效
	private boolean toObj = true;

	// 将查询结果转化为什么对象，可用任意对象，只要该对象有queryCols里面的别名对应的set方法即可
	private Class resultClass;

	// 投影，可用于获取最大，最小等一些聚合函数，如Projections.max("code")
	private List<Projection> projections;

	// 是否去除重复的数据, 必须有 QueryColumn，此值才有效
	private boolean isDistinct;

	public QueryMixture()
	{
	}

	public QueryMixture(List<QueryColumn> queryCols, List<Criterion> conditions, Class pojoClass, boolean toObj)
	{
		this.queryCols = queryCols;
		this.conditions = conditions;
		this.pojoClass = pojoClass;
		this.toObj = toObj;
	}

	/**
	 * 一般用于查询 一张表中 符合条件 的 某字段 聚合函数的属性（如某个字段最大或最小值）
	 * 
	 * @param projections
	 * @param conditions
	 * @param pojoClass
	 */
	public QueryMixture(List<Projection> projections, List<Criterion> conditions, Class pojoClass,
			List<JoinedTable> joinedTables)
	{
		this.projections = projections;
		this.conditions = conditions;
		this.pojoClass = pojoClass;
		this.joinedTables = joinedTables;
	}

	/**
	 * @param queryCols
	 * @param conditions
	 * @param pojoClass
	 * @param resultClass 查询结果返回的对象类型
	 */
	public QueryMixture(List<QueryColumn> queryCols, List<Criterion> conditions, Class pojoClass, Class resultClass)
	{
		this(queryCols, conditions, pojoClass, true);
		this.resultClass = resultClass;
	}

	public QueryMixture(List<QueryColumn> queryCols, List<Criterion> conditions, List<JoinedTable> joinedTables,
			Class pojoClass, Class resultClass)
	{
		this(queryCols, conditions, pojoClass, resultClass);
		this.joinedTables = joinedTables;
	}

	/**
	 * 见相应的set方法说明
	 */
	public List<Criterion> getConditions()
	{
		return conditions;
	}

	/**
	 * 查询条件, 可支持跨表查询，不过需要设置 joinedTables属性 (目前仅支持跨2张表的查询，即本表可查其关联表的关联表，而不可查关联表的关联表的关联表）
	 * 用符号"."来分割表和字段(字段和表用实体里面的属性名表示即可)。 如查询 ChannelOrgRelation中的Device中的Organization中的name, 即所属设备的组织名称， 可用
	 * Restrictions.eq("device.organization.name", "西湖区")
	 */
	public void setConditions(List<Criterion> conditions)
	{
		this.conditions = conditions;
	}

	/**
	 * 见相应的set方法说明
	 */
	public List<QueryColumn> getQueryCols()
	{
		return queryCols;
	}

	/**
	 * 返回哪些列，不设置 joinedTables也可支持查询非本表字段，不过表与表的类型默认为 inner join， 如果设置了joinedTable则使用设置的joinedTable
	 * 用符号"."来分割表和字段(字段和表用实体里面的属性名表示即可)。 如只想要获取ChannelOrgRelation中的Device中的Organization中的name，即所属设备的组织名称， 可用new
	 * QueryColumn("device.organization.name")， 1.) 想要将结果作为对象返回，则需要设置resultClass, 同时保证QueryColumn里面的别名跟
	 * resultClass的set方法对应，查询后会调用该set方法 2.) 否则，需要设置toObj = false
	 */
	public QueryMixture setQueryCols(List<QueryColumn> queryCols)
	{
		this.queryCols = queryCols;
		return this;
	}

	/**
	 * 见相应的set方法说明
	 */
	public List<JoinedTable> getJoinedTables()
	{
		return joinedTables;
	}

	/**
	 * 用于跨表查询的别名表，设置该属性后，可支持 conditions的跨表查询，以及支持 queryCols的非 inner join跨表返回列（目前仅支持跨2张表的查询） 用符号"."来分割表（表用实体里面的属性名表示即可）。
	 * 如查询或获取 ChannelOrgRelation中的Device中的Organization中的name，即所属设备的组织名称， 可用new JoinedTable("device.organization")
	 */
	public void setJoinedTables(List<JoinedTable> joinedTables)
	{
		this.joinedTables = joinedTables;
	}

	/**
	 * 见相应的set方法说明
	 */
	public List<Order> getOrders()
	{
		return orders;
	}

	/**
	 * 排序
	 */
	public void setOrders(List<Order> orders)
	{
		this.orders = orders;
	}

	/**
	 * 排序
	 */
	public void setOrder(Order order)
	{
		this.orders = Arrays.asList(order);
	}

	/**
	 * 见相应的set方法说明
	 */
	public Integer getStartIndex()
	{
		return startIndex;
	}

	/**
	 * 从第几条数据开始获取
	 */
	public void setStartIndex(Integer startIndex)
	{
		this.startIndex = startIndex;
	}

	/**
	 * 见相应的set方法说明
	 */
	public Integer getSize()
	{
		return size;
	}

	/**
	 * 一次性获取多少条件数据
	 */
	public void setSize(Integer size)
	{
		this.size = size;
	}

	/**
	 * 见相应的set方法说明
	 */
	public Class getPojoClass()
	{
		return pojoClass;
	}

	/**
	 * 作为本表的实体类
	 */
	public void setPojoClass(Class pojoClass)
	{
		this.pojoClass = pojoClass;
	}

	/**
	 * 见相应的set方法说明
	 */
	public boolean isToObj()
	{
		return toObj;
	}

	/**
	 * 是否将查询的结果转化为 对象，该属性只有在 queryCols有设置的 情况下才有效
	 */
	public void setToObj(boolean toObj)
	{
		this.toObj = toObj;
	}

	/**
	 * 见相应的set方法说明
	 */
	public Class getResultClass()
	{
		return resultClass;
	}

	/**
	 * 将查询结果转化为什么对象，可用任意对象，只要该对象有queryCols里面的别名对应的set方法即可
	 */
	public void setResultClass(Class resultClass)
	{
		this.resultClass = resultClass;
	}

	/**
	 * 见相应的set方法说明
	 */
	public List<Projection> getProjections()
	{
		return projections;
	}

	/**
	 * 投影，可用于获取最大，最小等一些聚合函数，如Projections.max("code")
	 */
	public void setProjections(List<Projection> projections)
	{
		this.projections = projections;
	}

	public static List<Criterion> asCriterionList(Criterion... criterions)
	{
		return Arrays.asList(criterions);
	}

	public static List<Projection> asProjectionList(Projection... projections)
	{
		return Arrays.asList(projections);
	}

	public boolean isDistinct()
	{
		return isDistinct;
	}

	public void setDistinct(boolean isDistinct)
	{
		this.isDistinct = isDistinct;
	}

}
